package com.example.changtest.ui

import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.webkit.JavascriptInterface
import android.webkit.ValueCallback
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.example.androidlib.utils.NormalUtil
import com.example.androidlib.wallet.TransMonitor
import com.example.androidlib.wallet.TransferToken
import com.example.androidlib.wallet.WalletTools
import com.example.androidlib.wallet.Web3jInfo
import com.example.changtest.R
import com.example.changtest.util.FileUtils
import com.example.changtest.util.GsonUtil
import com.example.changtest.util.LogUtil
import com.google.gson.Gson
import com.google.gson.JsonObject

import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.json.JSONObject
import org.web3j.crypto.Bip39Wallet
import org.web3j.crypto.CipherException
import org.web3j.crypto.WalletUtils
import org.web3j.protocol.core.DefaultBlockParameterName
import org.web3j.protocol.core.methods.response.*
import org.web3j.utils.Convert
import org.web3j.utils.Numeric
import java.io.File
import java.io.IOException
import java.math.BigDecimal
import java.math.BigInteger
import java.security.Security


class H5Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//        WXApiHelp.getInstance().init(this).wxLogin(object: RouterWXCallBack {
//            override fun callBack(callBack: Map<String?, String?>?) {
//
//            }
//        })

        // LogUtil.e("intent data2",intent?.dataString)
        val webView: WebView = findViewById<WebView>(R.id.webView)
        webView.settings.apply {
            //cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK  //关闭webview中缓存
            cacheMode = WebSettings.LOAD_NO_CACHE

            javaScriptEnabled = true  //设置支持javascript
            javaScriptCanOpenWindowsAutomatically = true  //支持通过JS打开新窗口
            allowFileAccess = true //设置可以访问文件

            loadsImagesAutomatically = true //支持自动加载图片
            defaultTextEncodingName = "utf-8"

            //设置自适应屏幕，两者合用
//            useWideViewPort = true  //将图片调整到适合webview的大小
//            loadWithOverviewMode = true // 缩放至屏幕的大小

            builtInZoomControls = true //设置支持缩放,设置了此属性，setSupportZoom(true);也默认设置为true

        }
//        val token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzE0MzEwNTcsImlzcyI6ImdhbWVfc2VydmVyIiwiRGF0YSI6eyJVc2VyTmFtZSI6IiIsIklEIjozNzA3MTN9fQ._Vq-8VEb_nN0aFGHqD1d4n0JsJw0tYSZd_cb6Tt0cTM"
//        webView.loadUrl("http://47.106.219.174:83/#/home?token=${token}")
//        webView.loadUrl("file:///android_asset/javascript.html")
        //webView.loadUrl("https://metamask.io")
        webView.loadUrl("file:///android_asset/metamask.html")

//        webView.webViewClient = object : WebViewClient() {
//            override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
//                if (url != null) {
//                    view?.loadUrl(url)
//                }
//                return true //返回值是true的时候控制去WebView打开，为false调用系统浏览器或第三方浏览器
//            }
//        }
        webView.addJavascriptInterface(NativeJsBridge(webView, this), "AndroidJsBridge")
        webView.addJavascriptInterface(
            Web3JNativeJsBridge(webView, this),
            "web3jExBridge"
        ) //可以多个

        //原生库 创建钱包 需要，否则异常
        setupBouncyCastle()
    }

    private fun setupBouncyCastle() {
        val provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)
            ?: // Web3j will set up the provider lazily when it's first used.
            return
        if (provider.javaClass == BouncyCastleProvider::class.java) {
            // BC with same package name, shouldn't happen in real life.
            return
        }
        // Android registers its own BC provider. As it might be outdated and might not include
        // all needed ciphers, we substitute it with a known BC bundled in the app.
        // Android's BC has its package rewritten to "com.android.org.bouncycastle" and because
        // of that it's possible to have another BC implementation loaded in VM.
        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
        Security.insertProviderAt(BouncyCastleProvider(), 1)
    }

    data class TokenObj(val token: String, val invitecode: String) {}

    class NativeJsBridge(val webView: WebView, val context: Activity) {
        @JavascriptInterface
        fun callAsLogin(text: String) {
            LogUtil.e("android callAsLogin 方法 $text")
            context.runOnUiThread {
                // java 调用 js方法
                //webView.loadUrl("javascript:setText('" + text + "')")
                webView.evaluateJavascript("javascript:setText('" + text + "')", ValueCallback {

                })
            }
//            context.runOnUiThread {
//                // java 调用 js方法
//                //webView.loadUrl("javascript:setText('" + text + "')")
//                  val callbackMethod = ""
//                  val params = ""
//                  webView.loadUrl( "javascript:$callbackMethod($params)")

//                webView.evaluateJavascript("javascript:setText('" + web3ClientVersion + "')", ValueCallback {
//
//                })
//            }
        }

        @JavascriptInterface
        fun callAsTokenString(): String {
            LogUtil.e("android callAsTokenString 方法")
            //{"token":"token","invitecode":"invitecode"}
//            var params = "{\"token\":\"token\",\"invitecode\":\"invitecode\"}"
//            return params
            var dd = JsonObject().apply {
                addProperty("token", "token")
                addProperty("invitecode", "inviteCode")
            }.toString()
            LogUtil.e("jsonObject", dd)
            return dd
        }

        @JavascriptInterface
        fun callAsTokenJson(): JSONObject {
            LogUtil.e("android callAsTokenJson 方法")
//            var dd =   JsonObject().apply {
//                addProperty("token","token")
//                addProperty("invitecode","inviteCode")
//            }
            // {"token":"token","invitecode":"inviteCode"}

            // try {
            var jsonObject = JSONObject()
            jsonObject.put("token", "token")
            jsonObject.put("invitecode", "inviteCode")
            //LogUtil.e("jsonObject",jsonObject)
            // {"nameValuePairs":{"token":"token","invitecode":"inviteCode"}}

            return jsonObject
//            } catch (e: Exception) {
//                LogUtil.e(e.toString())
//            }
//            return null
        }

//        @JavascriptInterface
//        fun callAsTokenJsonEval() {
//            LogUtil.e("android callAsTokenJsonEval 方法")
//
//            val jsonObject = JSONObject()
//            jsonObject.put("token","token123")
//            jsonObject.put("invitecode","inviteCode111")
//
//            // java 调用 js方法
//            //webView.loadUrl("javascript:callTokenJsonResult('" + jsonObject + "')")
//            webView.evaluateJavascript("javascript:callTokenJsonResult('" + jsonObject + "')", ValueCallback {
//
//            })
//        }


        @JavascriptInterface
        fun callAsString(json: String) {
            LogUtil.e("android callAsString 方法 $json") //{"token":"16","invitecode":"1"}
            val gson = Gson()
            val jsonObj = gson.fromJson(json, TokenObj::class.java)
            LogUtil.e("android callAsString 方法 ${jsonObj.token}")
        }

    }


    class Web3JNativeJsBridge(val webView: WebView, val context: Activity) {

        //连接节点
        //ccm节点 http://123.58.217.221:9933
        @JavascriptInterface
        fun connectClient(): String? {
            val web3j = Web3jInfo.connect()
            //测试是否连接成功
            val web3ClientVersion = web3j.web3ClientVersion().sendAsync().get().web3ClientVersion
            LogUtil.e("version=${web3ClientVersion}")
            return web3ClientVersion
        }

        //连接指定节点
        @JavascriptInterface
        fun connectClient(pointUrl: String): String? {
            val web3j = Web3jInfo.connect(pointUrl)
            //测试是否连接成功
            val web3ClientVersion = web3j.web3ClientVersion().sendAsync().get().web3ClientVersion
            LogUtil.e("version=${web3ClientVersion}")
            return web3ClientVersion
        }


        //创建一个钱包文件  3 种方式
        //1 创建完整类型的钱包
        //根据密码,创建json文件，返回文件名
        @JavascriptInterface
        fun createFullWallet(password: String): String {
            val walletFilePathDir = FileUtils.getExternalFileWalletDir(context) //钱包文件保持路径
            //LogUtil.e("${walletFilePathDir}")// /storage/emulated/0/Android/data/com.example.changtest/files/files/
            //var walletFileName = WalletUtils.generateFullNewWalletFile(password, File(walletFilePathDir)) //根据密码生成钱包到地址路径
            val walletFileName = WalletTools.createWalletFull(password, walletFilePathDir)
            LogUtil.e("createWalletFull ${walletFileName}")
            return walletFileName
        }

        //2 创建一个简单钱包
        //根据密码,创建json文件，返回文件名
        @JavascriptInterface
        fun createLightWallet(password: String): String {
            val walletFilePathDir = FileUtils.getExternalFileWalletDir(context) //钱包文件保持路径
            //LogUtil.e("${walletFilePathDir}")// /storage/emulated/0/Android/data/com.example.changtest/files/files/
            //val walletFileName = WalletUtils.generateLightNewWalletFile(password, File(walletFilePathDir))  //根据密码生成钱包到地址路径
            val walletFileName = WalletTools.createWalletLight(password, walletFilePathDir)
            LogUtil.e("createWalletLight ${walletFileName}") //UTC--2022-03-02T05-02-19.639000000Z--f09780dda2a3c4ba91f576218e92f162fd461854.json
            return walletFileName
        }

        //3 根据密码，助记词生成json文件,返回文件名和助记词
        //password 可为空,助记词随机生成 12个英文单词
        @JavascriptInterface
        fun createBip39Wallet(password: String?) {
            context.runOnUiThread {
                val walletFilePathDir = FileUtils.getExternalFileDestDir(context)
                val walletFileDestDir = FileUtils.getExternalFileDestDir(context)

                FileUtils.createOrExistsDir(File(walletFilePathDir))
                val bip39Wallet: Bip39Wallet = WalletUtils.generateBip39Wallet(password, File(walletFilePathDir)) //助记词钱包生成
                //LogUtil.e("助记词 ${bip39Wallet.mnemonic}")
                FileUtils.writeFileFromLineString(
                    File(walletFileDestDir),
                    bip39Wallet.mnemonic, true
                )

                val callbackMethod = "createBip39WalletCallback"
                val params:String? = GsonUtil.objToJsonString(bip39Wallet)
                LogUtil.e(params)
                webView.loadUrl("javascript:$callbackMethod($params)")
            }
        }


        //3 加载钱包种方式
        // 1根据 钱包文件路径 加载钱包
        @JavascriptInterface
        @Throws(IOException::class, CipherException::class)
        fun loadWallet(password: String, walletFileName: String): String? {
            val walletFilePathDir = FileUtils.getExternalFileWalletDir(context)//钱包文件保持路径
            val walletFilePath = walletFilePathDir + walletFileName

            //val credentials = WalletUtils.loadCredentials(password, walletFilePath)
            val credentials = WalletTools.loadWallet(password, walletFilePath)
            LogUtil.e("credentials", credentials)
            LogUtil.e("privateKey", Numeric.encodeQuantity(credentials.ecKeyPair.privateKey))
            //LogUtil.e("privateKey", "0x" + credentials.ecKeyPair.privateKey.toString(16))
            return GsonUtil.objToJsonString(credentials)
        }

        // 2根据私钥 加载钱包,
        // 返回 json
        @JavascriptInterface
        fun loadWalletByPrivateKey(inputPrivateKey: String): String? {
            LogUtil.e("privateKey: ${inputPrivateKey}")
            //val credentials = Credentials.create(inputPrivateKey)
            val credentials = WalletTools.loadWalletByPrivateKey(inputPrivateKey)
            LogUtil.e("credentials", credentials)
            LogUtil.e("privateKey", Numeric.encodeQuantity(credentials.ecKeyPair.privateKey))
            //LogUtil.e("privateKey", "0x" + credentials.ecKeyPair.privateKey.toString(16))
            return GsonUtil.objToJsonString(credentials)
        }

        // 3根据助记词加载钱包
        // password 密码可为空
        @JavascriptInterface
        fun loadWalletByMnemonic(password: String?, mnemonic: String): String? {
            //val credentials = WalletUtils.loadBip39Credentials(password, mnemonic) //no need password
            val credentials = WalletTools.loadWalletByMnemonic(password, mnemonic)
            LogUtil.e("credentials", credentials)
            LogUtil.e("privateKey", Numeric.encodeQuantity(credentials.ecKeyPair.privateKey))
            return GsonUtil.objToJsonString(credentials)
        }


        //查询指定地址的余额
        // 需要 1 链接节点
        @JavascriptInterface
        @Throws(IOException::class)
        fun getBlanceOf(address: String): String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }
            //第二个参数：区块的参数，建议选最新区块
            val ethgetbalance: EthGetBalance =
                web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).sendAsync().get()
            val balance: BigInteger = ethgetbalance.balance
            val blanceETH = Convert.fromWei(balance.toString(), Convert.Unit.ETHER).toPlainString()
            //val blanceETH = NormalUtil.getBalanceOfString(web3j,address)
            //LogUtil.e("余额：balance" + balance)
            LogUtil.e("余额：blanceETH " + blanceETH)
            return blanceETH
        }

        //交易笔数
        // 需要 1 链接节点
        @JavascriptInterface
        fun getNonce(address: String): String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }
            val nonce = NormalUtil.getNonce(web3j, address)
            println("交易笔数：$nonce")
            return nonce.toString()
        }

        //转账 发送以太币,5s以上
        // 需要 1 链接节点网络
        //     2 加载钱包  才可以进行转账
        /*
         * address:转入的钱包地址
         * amount:转入的金额
         */
        @JavascriptInterface
        fun transfer(addressTo: String, amount: Double): String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }

            val credentials = WalletTools.getCredentials()
            if (credentials == null) {
                Toast.makeText(context, "请加载钱包", Toast.LENGTH_SHORT).show()
                return null
            }
            //context.showDialog()
            var value = BigDecimal.valueOf(amount)
            val result = TransferToken.transferAsync(web3j, credentials, addressTo, value) ?: return null
            LogUtil.e("转账以太币：", result)
            //context.disDialog()
            return GsonUtil.objToJsonString(result)
        }

        //转账 自定义
        //GasPrice=---
        //gasLimit =最少21000
        // 需要 1 链接节点网络
        //     2 加载钱包  才可以进行转账
        @JavascriptInterface
        fun transferCustom(
            gasPriceOrigin: String,
            gasLimit: String,
            addressTo: String,
            amount: String
        ): String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }
            val credentials = WalletTools.getCredentials()
            if (credentials == null) {
                Toast.makeText(context, "请加载钱包", Toast.LENGTH_SHORT).show()
                return null
            }
            val value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger()
            val gasPrice = Convert.toWei(gasPriceOrigin, Convert.Unit.GWEI).toBigInteger()
            val gasLimit2 = gasLimit.toBigInteger()
            val result: EthSendTransaction = TransferToken.transferCustom(
                web3j, credentials, gasPrice, gasLimit2, addressTo, value)
            LogUtil.e("自定义 转账：", result)
            return GsonUtil.objToJsonString(result)
        }

        //无用
        @JavascriptInterface
        fun getAccountList():String?{
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }
            val ethAccountList = web3j.ethAccounts().sendAsync().get().accounts
            LogUtil.e("账号list", ethAccountList) //[]
            val chainId = web3j.ethChainId().sendAsync().get()
            LogUtil.e("chainId", chainId) // {"id":3,"jsonrpc":"2.0","result":"0x57"}
            return GsonUtil.objToJsonString(ethAccountList)
        }

        //转入
        @JavascriptInterface
        fun deposit(){
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return
            }
            val credentials = WalletTools.getCredentials()
            if (credentials == null) {
                Toast.makeText(context, "请加载钱包", Toast.LENGTH_SHORT).show()
                return
            }


        }
        //
        @JavascriptInterface
        fun withdraw(){

        }

        // ??
        //获取交易列表
        // 需要 1 链接节点
        @JavascriptInterface
        fun getTransactionList(blockNumber: Int): String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }

            val blockEthBlock: EthBlock = TransferToken.getBlockEthBlock(web3j, blockNumber)
            LogUtil.e("查询交易列表", blockEthBlock)
            return GsonUtil.objToJsonString(blockEthBlock)
        }

        //获取交易详情
        // 需要 1 链接节点
        @RequiresApi(Build.VERSION_CODES.N)
        @JavascriptInterface
        @Throws(IOException::class)
        fun getTransactionByHash(hash: String):String? {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return null
            }
// 有问题
//            val trans: EthTransaction = TransferToken.getTransactionByHash(web3j, hash)
//            val transaction: Transaction = trans.transaction.get()
//            LogUtil.e("params hash", transaction.hash)

            val tranReceipt = TransferToken.getTransactionReceiptByHash(web3j,hash)
            val receipt = tranReceipt.transactionReceipt.get()
            LogUtil.e("params", receipt)
            return GsonUtil.objToJsonString(receipt)
//            val callbackMethod = "getTransactionByHashCallback"
//            val params = GsonUtil.objToJsonString(receipt)
//            LogUtil.e(params)
//            webView.loadUrl("javascript:$callbackMethod($params)")
        }



        //监听 无效
        @JavascriptInterface
        fun web3jObserver() {
            val web3j = Web3jInfo.getWeb3j()
            if (web3j == null) {
                Toast.makeText(context, "请链接节点网络", Toast.LENGTH_SHORT).show()
                return
            }
            TransMonitor.getInstance().web3j = web3j
//            TransMonitor.getInstance().subscribeBlock {
//                LogUtil.e("tran", "------区块")
//                //System.out.println("block:" + it.getBlock().getHash() + ",author:" + it.getBlock().getAuthor());
//                LogUtil.e("tran", it)
//            }
            TransMonitor.getInstance().subscribeHasTrans {
                LogUtil.e("tran", "----------新交易事件")
                //System.out.println("from:" + it.getFrom() + ",to" + it.getTo());
                LogUtil.e("tran", it)
            }
            TransMonitor.getInstance().subscribePendingTrans {
                LogUtil.e("tran", "---------监听待定交易")
                //System.out.println("pending from:" + it.getFrom() + ",to" + it.getTo()+",gasPrice"+it.getGasPrice());
                LogUtil.e("tran", it)
            }


        }
        //锁定账号
//        @JavascriptInterface
//        fun lockAccount(address: String):Boolean{
//           var result = AccountUtil.lockAccount(address)
//            return result
//        }


    }

    override fun onDestroy() {
        val web3j = Web3jInfo.getWeb3j()
        web3j?.apply {
            this.shutdown()
        }
        super.onDestroy()
    }

}